﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace CommonEngine
{
    /// <summary>
    /// 实用数据操作
    /// </summary>
    public sealed class PragmaticAction
    {
        /// <summary>
        /// 输出到UI的字段过滤掉null
        /// </summary>
        public static void SetNullToDef(object en)
        {
            if (en == null)
                return;

            PropertyInfo[] ps = en.GetType().GetProperties();
            foreach (PropertyInfo p in ps)
            {
                var obj = p.GetValue(en, null);
                if (obj == null)
                {
                    if (p.PropertyType == typeof(string))
                        p.SetValue(en, string.Empty, null);
                }
            }
        }

        #region 泛型转DataTable

        /// <summary>
        /// 泛型集合List<T>转换为DataTable
        /// </summary>
        public static DataTable ToDataTable<T>(List<T> items)
        {
            var tb = new DataTable(typeof(T).Name);

            PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (PropertyInfo prop in props)
            {
                Type t = GetCoreType(prop.PropertyType);
                tb.Columns.Add(prop.Name, t);
            }

            foreach (T item in items)
            {
                var values = new object[props.Length];

                for (int i = 0; i < props.Length; i++)
                {
                    values[i] = props[i].GetValue(item, null);
                }

                tb.Rows.Add(values);
            }

            return tb;
        }
        private static Type GetCoreType(Type t)
        {
            if (t != null && IsNullable(t))
            {
                if (!t.IsValueType)
                {
                    return t;
                }
                else
                {
                    return Nullable.GetUnderlyingType(t);
                }
            }
            else
            {
                return t;
            }
        }
        private static bool IsNullable(Type t)
        {
            return !t.IsValueType || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
        }

        #endregion

        #region DataTable转泛型

        /// <summary>
        /// DataTable转泛型
        /// </summary>
        public static IList<T> ConvertToList<T>(DataTable table) where T : class
        {
            if (table == null)
                return null;

            List<DataRow> rows = new List<DataRow>();
            foreach (DataRow row in table.Rows)
                rows.Add(row);

            return ConvertTo<T>(rows);
        }
        private static IList<T> ConvertTo<T>(IList<DataRow> rows) where T : class
        {
            IList<T> list = null;
            if (rows != null)
            {
                list = new List<T>();
                foreach (DataRow row in rows)
                {
                    T item = CreateItem<T>(row);
                    list.Add(item);
                }
            }
            return list;
        }
        private static T CreateItem<T>(DataRow row) where T : class
        {
            T obj = default(T);
            if (row != null)
            {
                obj = Activator.CreateInstance<T>();
                foreach (DataColumn column in row.Table.Columns)
                {
                    PropertyInfo prop = obj.GetType().GetProperty(column.ColumnName);
                    try
                    {
                        object value = row[column.ColumnName];
                        prop.SetValue(obj, value, null);
                    }
                    catch
                    {
                        //You can log something here     
                        //throw;    
                    }
                }
            }
            return obj;
        }

        #endregion

        /// <summary>
        /// 数据格式转换DataTable->List<Dictionary<string, object>>
        /// </summary>
        public static List<Dictionary<string, object>> DtToLsDic(DataTable table)
        {
            List<Dictionary<string, object>> list = new List<Dictionary<string, object>>();

            int fieldCount = table.Columns.Count;
            string[] strArray = new string[fieldCount];
            Type[] typeArray = new Type[fieldCount];
            for (int i = 0; i < fieldCount; i++)
            {
                strArray[i] = table.Columns[i].ColumnName;
                typeArray[i] = table.Columns[i].DataType;
            }
            foreach (DataRow r in table.Rows)
            {
                Dictionary<string, object> item = new Dictionary<string, object>();
                for (int j = 0; j < fieldCount; j++)
                {
                    object obj2 = r[j];
                    if (obj2 is DBNull)
                    {
                        item.Add(strArray[j], null);
                    }
                    else
                    {
                        string s = obj2.ToString().Trim();
                        Type t = typeArray[j];
                        if (t == typeof(DateTime))
                        {
                            DateTime timeN = DateTime.Now;
                            if (DateTime.TryParse(s, out timeN))
                                item.Add(strArray[j], timeN);    //数据库来的数据能确认类型
                        }
                        else if (t == typeof(string))
                        {
                            if (string.IsNullOrEmpty(s))
                                item.Add(strArray[j], s);
                            else
                                item.Add(strArray[j], obj2.ToString());
                        }
                        else
                        {
                            var obj = Convert.ChangeType(obj2, t);
                            item.Add(strArray[j], obj);
                        }
                    }
                }

                if (item.First().Value == null || item.First().Value.ToString().Length < 1)
                    continue;
                else
                    list.Add(item);
            }
            return list;
        }

        /// <summary>
        /// DataTable-->Json
        /// </summary>
        /// <param name="dt">DataTable</param>
        /// <returns>Json</returns>
        public static string DataTableConvertToJson(DataTable dt)
        {
            StringBuilder jsonBuilder = new StringBuilder();

            // 拼接JSON串
            jsonBuilder.Append("{\"");
            jsonBuilder.Append(dt.TableName);
            jsonBuilder.Append("\":[");
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                jsonBuilder.Append("{");
                for (int j = 0; j < dt.Columns.Count; j++)
                {
                    jsonBuilder.Append("\"");
                    jsonBuilder.Append(dt.Columns[j].ColumnName);
                    jsonBuilder.Append("\":\"");
                    jsonBuilder.Append(dt.Rows[i][j].ToString());
                    jsonBuilder.Append("\",");
                }
                jsonBuilder.Remove(jsonBuilder.Length - 1, 1);
                jsonBuilder.Append("},");
            }
            if (dt.Rows.Count > 0) jsonBuilder.Remove(jsonBuilder.Length - 1, 1);
            jsonBuilder.Append("]");
            jsonBuilder.Append("}");

            return jsonBuilder.ToString();
        }

        #region---实体查询应用---

        /// <summary>
        /// 取实体类的主键名与类型
        /// </summary>
        public static Type GetPrimaryNameAndType<T>(out string proName) where T : class
        {
            proName = null;
            Type result = null;
            var prLs = typeof(T).GetProperties();
            foreach (var p in prLs)
            {
                var colAtt = Attribute.GetCustomAttributes(p, typeof(KeyAttribute));
                if (colAtt.Any())
                {
                    proName = p.Name;
                    result = p.PropertyType;
                    break;
                }
            }
            return result;
        }
        /// <summary>
        /// 取实体类的主键名与值
        /// </summary>
        public static object GetPrimaryNameAndValue<T>(T en, out string proName) where T : class
        {
            proName = null;
            if (en == null)
                return null;

            object result = null;
            var prLs = typeof(T).GetProperties();
            foreach (var p in prLs)
            {
                var colAtt = Attribute.GetCustomAttributes(p, typeof(KeyAttribute));
                if (colAtt.Any())
                {
                    proName = p.Name;
                    result = p.GetValue(en, null);
                    break;
                }
            }
            return result;
        }
        /// <summary>
        /// 获取实体主键的值
        /// </summary>
        public static object GetPrimaryVal<T>(T en)
        {
            if (en == null)
                return null;

            var pri = typeof(T).GetProperties();
            foreach (var i in pri)
            {
                var pris = i.GetCustomAttributes(typeof(KeyAttribute), true);
                if (pris.Any())
                {
                    return i.GetValue(en, null);
                }
            }
            return null;
        }
        /// <summary>
        /// 获取实体对象表名
        /// </summary>
        public static string GetEntityTable(Type objTye)
        {
            string entityName = objTye.Name;
            var tableAttribute = objTye.GetCustomAttributes(true).OfType<TableAttribute>();
            var descriptionAttributes = tableAttribute as TableAttribute[] ?? tableAttribute.ToArray();
            if (descriptionAttributes.Any())
                entityName = descriptionAttributes.ToList()[0].Name;

            return entityName;
        }

        /// <summary>
        /// 获取主键表达式
        /// </summary>
        public static Expression<Func<U, bool>> GetPrimaryExp<U>(object ID) where U : class
        {
            string ProName = null;
            Type t = GetPrimaryNameAndType<U>(out ProName);
            if (string.IsNullOrWhiteSpace(ProName))
                return (a => false);
            else
                return ProName.MakeEqualExp<U>(ID);
        }
        /// <summary>
        /// 获取实体对象的主键表达式
        /// </summary>
        public static Expression<Func<U, bool>> GetEntityPrimaryExp<U>(U en) where U : class
        {
            if (en == null)
                return (a => 1 == 0);

            string ProName = null;
            object ID = GetPrimaryNameAndValue<U>(en, out ProName);
            if (string.IsNullOrWhiteSpace(ProName))
                return (a => false);
            else
                return ProName.MakeEqualExp<U>(ID);
        }
        /// <summary>
        /// 输出主键表达式2 a=> Ls`.Contains(a.ID)
        /// 针对实体集合输出过滤表达式---这样的操作完成针对配置---配置好了就OK
        /// </summary>
        public static Expression<Func<U, bool>> GetPrimaryContainsExp<U>(List<U> Ls) where U : class
        {
            if (Ls == null || Ls.Count < 1)
                return (a => 1 == 0);

            string proName = null;
            Type proT = GetPrimaryNameAndType<U>(out proName);

            if (proT == typeof(int))
            {
                var int_exp = proName.MakeFuncExp<U, int>();
                var int_ls = Ls.Select(int_exp.Compile()).ToList();
                return proName.MakeContainsExp<U>(int_ls);
            }
            else if (proT == typeof(long))
            {
                var long_exp = proName.MakeFuncExp<U, long>();
                var long_ls = Ls.Select(long_exp.Compile()).ToList();
                return proName.MakeContainsExp<U>(long_ls);
            }
            else if (proT == typeof(string))
            {
                var exp = proName.MakeFuncExp<U, string>();
                var ls = Ls.Select(exp.Compile()).ToList();
                return proName.MakeContainsExp<U>(ls);
            }
            else
                return (a => 1 == 0);
        }

        #endregion

        #region---权限判断(位域)---

        /// <summary>
        /// 判断权限，P表示“接权值”（可变的判断值）
        /// </summary>
        public static bool Has_Rights(long P, long FixVal)
        {
            return ((P & FixVal) == FixVal) ? FixVal == 0 ? false : true : false;
        }
        public static bool Has_Rights(int P, int FixVal)
        {
            return ((P & FixVal) == FixVal) ? FixVal == 0 ? false : true : false;
        }

        /// <summary>
        /// 添加权限，P表示“接权值”（返回的值就要赋值回P）
        /// </summary>
        public static long Add_Rights(long P, long FixVal)
        {
            return (P | FixVal);
        }
        public static int Add_Rights(int P, int FixVal)
        {
            return (P | FixVal);
        }

        /// <summary>
        /// 删除权限，P表示“接权值”（返回的值就要赋值回P）
        /// </summary>
        public static long Del_Rights(long P, long FixVal)
        {
            return (P & ~FixVal);
        }
        public static int Del_Rights(int P, int FixVal)
        {
            return (P & ~FixVal);
        }

        #endregion

        #region---基础常规扩展---

        public static bool IsGuid(string expression)
        {
            if (expression != null)
            {
                Regex guidRegEx = new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");
                return guidRegEx.IsMatch(expression);
            }
            return false;
        }

        /// <summary>
        /// 校验地区代码是否有效
        /// </summary>
        public static bool IsAreaCode(string input)
        {
            if ((!IsNumber(input) || (input.Length < 3)) || (input.Length > 5))
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 手机号码有效性验证
        /// </summary>
        /// <param name="mobile">手机号码字符串</param>
        /// <returns>有效:true,否则:false</returns>
        public static bool IsValidMobile(string mobile)
        {
            return Regex.IsMatch(mobile, @"^(13|15|18|14)\d{9}$");
        }

        /// <summary>
        /// 校验数据是否有效的十进制数
        /// </summary>
        public static bool IsDecimal(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return false;
            }
            return Regex.IsMatch(input, "^[0-9]+[.]?[0-9]+$");
        }

        /// <summary>
        /// 校验数据是否有效的十进制数，允许带有正负号
        /// </summary>
        public static bool IsDecimalSign(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return false;
            }
            return Regex.IsMatch(input, "^[+-]?[0-9]+[.]?[0-9]+$");
        }

        /// <summary>
        /// 校验输入的Email格式是否正确
        /// </summary>
        public static bool IsEmail(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return false;
            }
            return Regex.IsMatch(input, @"^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
        }

        /// <summary>
        /// 校验输入的数据是否有效的IP
        /// </summary>
        public static bool IsIP(string input)
        {
            return (!string.IsNullOrEmpty(input) && Regex.IsMatch(input.Trim(), @"^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$"));
        }

        /// <summary>
        /// 校验输入的数据是否有效的整形数字 正数
        /// </summary>
        public static bool IsNumber(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return false;
            }
            return Regex.IsMatch(input, "^[0-9]+$");
        }

        /// <summary>
        /// 校验输入的数据是否有效的正负整形数字
        /// </summary>
        public static bool IsNumberSign(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return false;
            }
            return Regex.IsMatch(input, "^[+-]?[0-9]+$");
        }

        /// <summary>
        /// 校验输入的邮政编码是否有效
        /// </summary>
        public static bool IsPostCode(string input)
        {
            if (!(IsNumber(input) && (input.Length == 6)))
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 校验输入的数据是否有效的URL
        /// </summary>
        public static bool IsUrl(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return false;
            }
            return Regex.IsMatch(input, @"^http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?$");
        }

        /// <summary>
        /// 校验输入数据是否有效ID
        /// </summary>
        public static bool IsValidId(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return false;
            }
            input = input.Replace("|", "").Replace(",", "").Replace("-", "").Replace(" ", "").Trim();
            if (string.IsNullOrEmpty(input))
            {
                return false;
            }
            return IsNumber(input);
        }

        /// <summary>
        /// 校验用户名是否有效
        /// </summary>
        public static bool IsValidUserName(string userName)
        {
            if (string.IsNullOrEmpty(userName))
            {
                return false;
            }
            if (userName.Length > 20)
            {
                return false;
            }
            if (userName.Trim().Length == 0)
            {
                return false;
            }
            if (userName.Trim(new char[] { '.' }).Length == 0)
            {
                return false;
            }
            string str = "\\/\"[]:|<>+=;,?*@";
            for (int i = 0; i < userName.Length; i++)
            {
                if (str.IndexOf(userName[i]) >= 0)
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 判断是否数字
        /// </summary>
        public static bool isNumber(object obj)
        {
            if (obj != DBNull.Value)
            {
                try
                {
                    Convert.ToDouble(obj);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        #endregion

        #region---Levenshtein Distance(编辑距离)---

        /// <summary>
        /// Compute the distance between two strings.
        /// </summary>
        public static int LevenshteinDistance(string s, string t)
        {
            int n = s.Length;
            int m = t.Length;
            int[,] d = new int[n + 1, m + 1];

            // Step 1
            if (n == 0)
                return m;

            if (m == 0)
                return n;

            // Step 2
            for (int i = 0; i <= n; d[i, 0] = i++)
            {
            }
            for (int j = 0; j <= m; d[0, j] = j++)
            {
            }

            // Step 3
            for (int i = 1; i <= n; i++)
            {
                //Step 4
                for (int j = 1; j <= m; j++)
                {
                    // Step 5
                    int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
                    // Step 6
                    d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost);
                }
            }
            // Step 7
            return d[n, m];
        }

        #endregion

    }
}
